package com.doubledragon.tiny.ioc.factory;

import com.doubledragon.tiny.aop.autoProxyCreator.BeanPostProcessor;
import com.doubledragon.tiny.ioc.BeanDefinition;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by dragon
 *
 * BeanFactory接口的抽象实现, 实现了Bean的获取和创建,
 * 而beanDefinitionMap是BeanDefinition的容器, BeanFactory 会利用注册进来的BeanDefinition实例化bean
 */
public abstract class AbstractBeanFactory implements BeanFactory{
    //在读取xml文件并解析出一堆BeanDefinition后，所有的BeanDefinition会被注册到这里，等到下次getBean时，利用BeanDefinition去实例化bean
    //在spring中，BeanDefinition会被注册到BeanDefinitionRegistry类的ConcurrentHashMap中
    protected final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //所有的beanName的集合，用于遍历获取bean
    private final List<String> beanNames = new ArrayList<>();

    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    //beanName为<bean>节点的id，或者alias，
    @Override
    public Object getBean(String beanName) throws Exception{
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition == null){
            throw new IllegalArgumentException("No bean named " + beanName + " is defined");
        }else{
            //如果已经实例化过 bean，则不需要再重复创建
            Object bean = beanDefinition.getBean();
            if(bean == null){
                //创建普通的bean
                bean = doCreateBean(beanDefinition);
                // 初始化bean，在初始化前后会调用bean的前/后置处理器，
                // 当创建的bean有匹配的poincut表达式对应的增强器时，就会生成一个代理bean
                bean = initializingBean(beanName, bean);
            }
            return bean;
        }
    }

    /**
     * 用于注册BeanDefinition。
     * 此处的入参BeanDefinition，在spring中有多种方式可以创建,它包含了配置文件中<bean>节点的各种属性，如id，name()，class，scope等。。
     * 如AbstractBeanFactory的其中一个实现:XmlBeanFactory，就是解析spring的xml配置后创建BeanDefinition
     * XmlBeanFactory使用了自己定义的XML读取器XmlBeanDefinitionReader实现了个性化的BeanDefinitionReader读取.
     */
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws Exception{
        beanDefinitionMap.put(beanName, beanDefinition);
        beanNames.add(beanName);
    }

    //初始化bean
    public abstract Object doCreateBean(BeanDefinition beanDefinition) throws Exception;


    // 在初始化bean前/后，调用bean的前/后置处理器
    // 当创建的bean有匹配的poincut表达式对应的增强器时，就会生成一个代理bean
    private Object initializingBean(String beanName, Object bean) throws Exception {
        //调用bean的前置处理器
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
        }

        //调用bean的初始化方法
        // 比如 InitializingBean接口的afterPropertiesSet方法或者 @PostContruct注解的方法
        // if (bean.getClass().isAssignableFrom(InitializingBean.class)){
        //    InitializingBean initializingBean = (InitializingBean) bean;
        //    initializingBean.afterPropertiesSet();
        // }

        //调用bean的后置处理器
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

    /**
     * 寻找bean集合，它们是beanType类型的子类
     *
     *
     * @param beanType bean类型
     * @return
     * @throws Exception
     */
    public List getBeanByType(Class beanType) throws Exception {
        List result = new ArrayList();
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanType.isAssignableFrom(beanDefinition.getBeanClass())){
                // 该方法会在load完beanDefinition后就被调用，此时还没有生成bean，
                // 为了提前初始化BeanPostPocessor，所以先去getBean()来创建bean
                Object bean = getBean(beanName);
                result.add(bean);
            }
        }
        return result;
    }




    //getter setter
    public Map<String, BeanDefinition> getBeanDefinitionMap() {
        return beanDefinitionMap;
    }

    public List<String> getBeanNames() {
        return beanNames;
    }

    public List<BeanPostProcessor> getBeanPostProcessors() {
        return beanPostProcessors;
    }
}
